/**
 * @author Paulo Faria Reis
 * @created 15 Mar 2012
 *
 */
package networkoperations;

import controller.DataStore;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.MulticastSocket;

/**
 * @author Paulo Faria Reis
 */
public class MulticastListener implements Runnable{
   DataStore dataStorage;
   //	String request;		// Message received by the server.
   //	byte[] msgOut;		// Message to send in the packet.
   byte[] msgIn = new byte[1024];		// Received message.
   String controlPayload;
   MulticastSocket controlSocket = null;
   private volatile boolean dontStop = true;

   /**
    *  Constructor.
    */
   public MulticastListener(DataStore dataStor) {
	dataStorage = dataStor;
   }

   /**
    *  Listens to the multicast control channel.
    *  Creates a new Replier thread for each SEARCH received.
    *  Creates a new Sender thread for each GET that it has a chunk.
    */
   @Override
   public void run(){
	DatagramPacket controlPack = new DatagramPacket(msgIn, msgIn.length);
	// Creates socket and join the group.
	try {
	   controlSocket = new MulticastSocket(dataStorage.getControlPort());
	   controlSocket.joinGroup(dataStorage.getMcAddress());
	} catch (IOException e1) {
	   if(dataStorage.debug()) {
		System.out.println("I/O Invalid while trying to create socket and join group!");
		e1.printStackTrace();
	   }
	}

	// Keeps listening for packets and process them.
	while(dataStorage.keepWorking() && dontStop) {
	   if(dataStorage.debug()) System.out.println("Ready to receive messages...");
	   try {
		controlSocket.receive(controlPack);
	   }
	   catch (IOException e) {
		if(dataStorage.debug()) {
		   System.out.println("I/O Invalid getting datagram from socket!");
		   e.printStackTrace();
		}
	   }

	   if(!(dataStorage.sentFromThisHost(controlPack.getAddress()))) {	// If it's a packet sent from this host, just ignore it.
		controlPayload = new String(controlPack.getData(), 0, controlPack.getLength());	// Get Datagram payload in string format.
		if(dataStorage.debug()){	// Used for debug.
		   System.out.println("Sender: " + controlPack.getAddress().toString() + ":" + controlPack.getPort());
		   System.out.println("Size: " + controlPack.getLength());
		   System.out.println("Message: " + controlPayload);
		}

		// For each request for a file search launch a thread to answer.
		if(controlPayload.startsWith("SEARCH"))
		{
		   Replier replyToSearchRequest = new Replier(dataStorage, controlPack.getData().toString());
		   replyToSearchRequest.start();
		}
		// For each file requested start a thread to send (chunks of) the file.
		else if(controlPayload.startsWith("GET")){
		   String[] splited = controlPayload.split(" ");	// GET sha [n1,n2]|[n1-n2]|[n]
		   if(splited.length > 2) {
			if(splited[2].contains(",")) {	// Chunk comma separated list.
			   dataStorage.newUploadStarted(splited[1], splited[2].split(","));
			}   
			else if(splited[2].contains("-")) {
			   String[] minMax = splited[2].split("-");
			   dataStorage.newUploadStarted(splited[1], Integer.getInteger(minMax[0]), Integer.getInteger(minMax[1]));
			}
			else {
			   dataStorage.newUploadStarted(splited[1], Integer.getInteger(splited[2]));
			}
		   }
		}
		else if(controlPayload.startsWith("FOUND")){	// FOUND id sha size filename
		   dataStorage.processFound(controlPayload);
		}
	   }
	}	

	// Time to go, leaving group and closing socket.
	try {
	   controlSocket.leaveGroup(dataStorage.getMcAddress());
	} catch (IOException e) {
	   System.out.println("I/O Invalido leaving group!");
	   e.printStackTrace();
	}
	controlSocket.close();
   }	

   /**
    *  Stops the thread.
    */
   public void stopWork() {
	dontStop = false;
   }
}
